package com.googlecode.lookie.core.commands;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.richclient.application.support.ApplicationWindowCommandManager;
import org.springframework.richclient.command.AbstractCommand;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.command.CommandGroupFactoryBean;
import org.springframework.richclient.command.InvalidGroupMemberEncodingException;

public class PlatformCommandManager
    extends ApplicationWindowCommandManager
    implements ApplicationContextAware
{

    private static final Logger logger = LoggerFactory.getLogger( PlatformCommandManager.class );

    private ApplicationContext applicationContext;

    private Map<String, List<CommandContribution>> contributions;

    public void setApplicationContext( ApplicationContext applicationContext )
    {
        this.applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext()
    {
        return applicationContext;
    }

    protected Map<String, List<CommandContribution>> getContributions()
    {
        if ( contributions == null )
        {
            contributions = createContributions();
        }
        return contributions;
    }

    protected Map<String, List<CommandContribution>> createContributions()
    {
        Map<String, List<CommandContribution>> map = new HashMap<String, List<CommandContribution>>();

        Object osgiBeans = getApplicationContext().getBean( "commandContributions" );
        for ( Object bean : ( (List<?>) osgiBeans ) )
        {
            CommandContribution contribution = (CommandContribution) bean;
            List<CommandContribution> list = map.get( contribution.getGroupId() );
            if ( list == null )
            {
                list = new ArrayList<CommandContribution>();
                map.put( contribution.getGroupId(), list );
            }
            list.add( contribution );
        }

        return map;
    }

    @Override
    public void registerCommand( AbstractCommand command )
    {
        if ( command instanceof CommandGroup )
        {
            CommandGroup group = (CommandGroup) command;
            List<CommandContribution> contribs = getContributions().get( group.getId() );
            if ( contribs != null )
            {
                for ( CommandContribution contrib : contribs )
                {
                    contributeCommand( contrib, group );
                }
            }
        }
        super.registerCommand( command );
    }

    /**
     * Iterates over the collection of encoded members and adds them to the given command group.
     *
     * @param group The group that is to contain the commands from the encoded members list. Must not be null.
     * @throws InvalidGroupMemberEncodingException if a member prefix is provided without a command id.
     */
    protected void contributeCommand( CommandContribution contrib, CommandGroup group )
    {
        Object o = contrib.getCommand();

        if ( o instanceof AbstractCommand )
        {
            addCommand( (AbstractCommand) o, contrib.getGroupMemberPath(), group );
        }
        else if ( o instanceof String )
        {
            String str = (String) o;
            if ( str.startsWith( CommandGroupFactoryBean.COMMAND_MEMBER_PREFIX ) )
            {
                String commandId = str.substring( CommandGroupFactoryBean.COMMAND_MEMBER_PREFIX.length() );
                addCommandMember( commandId, contrib.getGroupMemberPath(), group );
            }
            else if ( str.startsWith( CommandGroupFactoryBean.GROUP_MEMBER_PREFIX ) )
            {
                String commandId = str.substring( CommandGroupFactoryBean.GROUP_MEMBER_PREFIX.length() );
                addCommandMember( commandId, contrib.getGroupMemberPath(), group );
            }
            else
            {
                addCommandMember( str, contrib.getGroupMemberPath(), group );
            }
        }
    }

    private void addCommand( AbstractCommand command, String memberPath, CommandGroup group )
    {
        logger.info( String.format( "contributing %s to %s", command.getId(), group.getId() ) );
        if ( memberPath == null )
        {
            group.add( command, true );
        }
        else
        {
            group.add( memberPath, command, true );
        }
    }

    /**
     * Adds the command object with the given id to the given command group. If a command registry has not yet been
     * provided to this factory, the command id will be passed as a 'lazy placeholder' to the group instead.
     *
     * @param commandId The id of the command to be added to the group. This is expected to be in decoded form, i.e. any
     *            command prefixes have been removed. Must not be null.
     * @param isGroup indicates if the command is actually a command group.
     * @param group The group that the commands will be added to. Must not be null.
     */
    private void addCommandMember( String commandId, String memberPath, CommandGroup group )
    {
        AbstractCommand command = (AbstractCommand) getCommand( commandId );
        if ( command != null )
        {
            addCommand( command, memberPath, group );
        }

    }

}
